﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace PrinterMonitoring
{
    public partial class FormPrinterMonitoring : Form
    {
        //存放已分析内容
        private DataTable jobHisTab;
        public FormPrinterMonitoring()
        {
            InitializeComponent();
            jobHisTab = new DataTable();
            jobHisTab.Columns.Add("打印机名", typeof(string));
            jobHisTab.Columns.Add("jobID", typeof(int));
            jobHisTab.Columns.Add("时间", typeof(DateTime));
            Task.Run(() => {
                while(true)
                {
                    List<Win32API.PRINTER_INFO_2> usbPrintList = GetUsbPrintList();
                    EmumPrintJob(usbPrintList);
                    Thread.Sleep(500);
                }
            });
        }

        /// <summary>
        /// 获得系统已安装的打印机
        /// </summary>
        /// <returns></returns>
        private List<Win32API.PRINTER_INFO_2> GetSysPrintList()
        {
            uint iNeeded = 0;
            uint iReturned = 0;
            bool ret = Win32API.EnumPrinters(Win32API.PrinterEnumFlags.PRINTER_ENUM_LOCAL, null, 2, IntPtr.Zero, 0, ref iNeeded, ref iReturned);
            IntPtr pAddr = Marshal.AllocHGlobal((int)iNeeded);
            ret = Win32API.EnumPrinters(Win32API.PrinterEnumFlags.PRINTER_ENUM_LOCAL, null, 2, pAddr, iNeeded, ref iNeeded, ref iReturned);
            if (ret)
            {
                List<Win32API.PRINTER_INFO_2> printList = new List<Win32API.PRINTER_INFO_2>();
                int offset = pAddr.ToInt32();
                for (int i = 0; i < iReturned; i++)
                {
                    Win32API.PRINTER_INFO_2 pRINTER_INFO_2 = new Win32API.PRINTER_INFO_2();
                    pRINTER_INFO_2.pServerName = Marshal.PtrToStringAuto(Marshal.ReadIntPtr(new IntPtr(offset)));
                    offset += 4;
                    pRINTER_INFO_2.pPrinterName = Marshal.PtrToStringAuto(Marshal.ReadIntPtr(new IntPtr(offset)));
                    offset += 4;
                    pRINTER_INFO_2.pShareName = Marshal.PtrToStringAuto(Marshal.ReadIntPtr(new IntPtr(offset)));
                    offset += 4;
                    pRINTER_INFO_2.pPortName = Marshal.PtrToStringAuto(Marshal.ReadIntPtr(new IntPtr(offset)));
                    offset += 4;
                    pRINTER_INFO_2.pDriverName = Marshal.PtrToStringAuto(Marshal.ReadIntPtr(new IntPtr(offset)));
                    offset += 4;
                    pRINTER_INFO_2.pComment = Marshal.PtrToStringAuto(Marshal.ReadIntPtr(new IntPtr(offset)));
                    offset += 4;
                    pRINTER_INFO_2.pLocation = Marshal.PtrToStringAuto(Marshal.ReadIntPtr(new IntPtr(offset)));
                    offset += 4;
                    //pRINTER_INFO_2.pDevMode = Marshal.ReadIntPtr(new IntPtr(offset));
                    pRINTER_INFO_2.pDevMode = (Win32API.DEVMODE)Marshal.PtrToStructure(new IntPtr(offset), typeof(Win32API.DEVMODE));

                    offset += 4;
                    pRINTER_INFO_2.pSepFile = Marshal.PtrToStringAuto(Marshal.ReadIntPtr(new IntPtr(offset)));
                    offset += 4;
                    pRINTER_INFO_2.pPrintProcessor = Marshal.PtrToStringAuto(Marshal.ReadIntPtr(new IntPtr(offset)));
                    offset += 4;
                    pRINTER_INFO_2.pDatatype = Marshal.PtrToStringAuto(Marshal.ReadIntPtr(new IntPtr(offset)));
                    offset += 4;
                    pRINTER_INFO_2.pParameters = Marshal.PtrToStringAuto(Marshal.ReadIntPtr(new IntPtr(offset)));
                    offset += 4;
                    pRINTER_INFO_2.pSecurityDescriptor = Marshal.ReadIntPtr(new IntPtr(offset));
                    offset += 4;
                    pRINTER_INFO_2.Attributes = (uint)Marshal.ReadIntPtr(new IntPtr(offset));
                    offset += 4;
                    pRINTER_INFO_2.Priority = (uint)Marshal.ReadInt32(new IntPtr(offset));
                    offset += 4;
                    pRINTER_INFO_2.DefaultPriority = (uint)Marshal.ReadInt32(new IntPtr(offset));
                    offset += 4;
                    pRINTER_INFO_2.StartTime = (uint)Marshal.ReadInt32(new IntPtr(offset));
                    offset += 4;
                    pRINTER_INFO_2.UntilTime = (uint)Marshal.ReadInt32(new IntPtr(offset));
                    offset += 4;
                    pRINTER_INFO_2.Status = (uint)Marshal.ReadInt32(new IntPtr(offset));
                    offset += 4;
                    pRINTER_INFO_2.cJobs = (uint)Marshal.ReadInt32(new IntPtr(offset));
                    offset += 4;
                    pRINTER_INFO_2.AveragePPM = (uint)Marshal.ReadInt32(new IntPtr(offset));
                    offset += 4;
                    printList.Add(pRINTER_INFO_2);

                }
                Marshal.FreeHGlobal(pAddr);
                return printList;
            }
            else
            {
                return new List<Win32API.PRINTER_INFO_2>();
            }
        }

        /// <summary>
        /// 获得USB连接的打印机
        /// </summary>
        /// <returns></returns>
        private List<Win32API.PRINTER_INFO_2> GetUsbPrintList()
        {
            List<Win32API.PRINTER_INFO_2> sysPrintList = GetSysPrintList();
            List<Win32API.PRINTER_INFO_2> usbPrintList = new List<Win32API.PRINTER_INFO_2>();
            usbPrintList = sysPrintList.Where(p => p.pPortName == "USB001").ToList();
            return sysPrintList; //暂时全部返回
            return usbPrintList;
        }

        private void EmumPrintJob(List<Win32API.PRINTER_INFO_2> printList)
        {
            foreach (var print in printList)
            {
                IntPtr handle;
                int firstJob = 0;
                int numJobs = 127;
                int pcbNeeded;
                int pcReturned;
                Win32API.OpenPrinter(print.pPrinterName, out handle, IntPtr.Zero);
                Win32API.EnumJobs(handle, firstJob, numJobs, 1, IntPtr.Zero, 0, out pcbNeeded, out pcReturned);
                IntPtr pData = Marshal.AllocHGlobal(pcbNeeded);
                Win32API.EnumJobs(handle, firstJob, numJobs, 1, pData, pcbNeeded, out pcbNeeded, out pcReturned);

                Win32API.JOB_INFO_1[] jobs = new Win32API.JOB_INFO_1[pcReturned];

                int pTemp = pData.ToInt32();
                for (int i = 0; i < pcReturned; ++i)
                {
                    jobs[i] = (Win32API.JOB_INFO_1)Marshal.PtrToStructure(new IntPtr(pTemp), typeof(Win32API.JOB_INFO_1));
                    pTemp += Marshal.SizeOf(typeof(Win32API.JOB_INFO_1));
                }
                Marshal.FreeHGlobal(pData);

                foreach (var job in jobs)
                {
                    DateTime jobTime = Convert.ToDateTime($"{job.Submitted.wYear}-{job.Submitted.wMonth}-{job.Submitted.wDay} {job.Submitted.wHour}:{job.Submitted.wMinute}:{job.Submitted.wSecond}");
                    DataRow[] dataRows = jobHisTab.Select($"打印机名='{job.pPrinterName}' and jobID='{job.JobId}' and 时间='{jobTime}'");
                    if(dataRows.Length==0)
                    {
                        jobHisTab.Rows.Add(job.pPrinterName, job.JobId, jobTime);
                        GetPrinterJob(handle, job.JobId);
                    }
                }
                Win32API.ClosePrinter(handle);
            }
        }

        private void GetPrintInfo(IntPtr handle)
        {
            UInt32 lNeeded = 0;
            UInt32 lRetrieve = 0;
            //获取任务信息的字节数
            bool ret = Win32API.GetPrinter(handle, 2, IntPtr.Zero, 0, out lNeeded);
            lRetrieve = lNeeded;
            IntPtr pPrinter = Marshal.AllocHGlobal((int)lRetrieve);
            ret = Win32API.GetPrinter(handle, 2, pPrinter, lRetrieve, out lNeeded);
            if (ret == true)
            {
                int pTemp = pPrinter.ToInt32();
            }
        }

        private void GetPrinterJob(IntPtr handle,int jobID)
        {
            UInt32 lNeeded = 0;
            UInt32 lRetrieve = 0;
            //获取任务信息的字节数
            bool ret = Win32API.GetJob(handle, jobID, 2, IntPtr.Zero, 0, out lNeeded);
            lRetrieve = lNeeded;
            IntPtr pData = Marshal.AllocHGlobal((int)lRetrieve);
            ret = Win32API.GetJob(handle, jobID, 2, pData, lRetrieve, out lNeeded);

            if (ret == true)//如果获取成功，则进行分析
            {
                //根据level使用对应的结构体
                int pTemp = pData.ToInt32();//start pointer 
                Win32API.JOB_INFO_2 info2 = new Win32API.JOB_INFO_2();
                info2 = (Win32API.JOB_INFO_2)Marshal.PtrToStructure
                    (new IntPtr(pTemp), typeof(Win32API.JOB_INFO_2));

                int intRet = System.Convert.ToInt32(info2.Status);

                Win32API.JOB_INFO_2 jobinfo2 = new Win32API.JOB_INFO_2();
                jobinfo2 = (Win32API.JOB_INFO_2)Marshal.PtrToStructure(new IntPtr(pTemp), typeof(Win32API.JOB_INFO_2));

                Win32API.DEVMODE devm = new Win32API.DEVMODE();
                devm = (Win32API.DEVMODE)Marshal.PtrToStructure(jobinfo2.pDevMode, typeof(Win32API.DEVMODE));

                //双面类型
                string duplex = "获取失败";
                switch (devm.dmDuplex)
                {
                    case 1:
                        duplex = "单面";
                        break;
                    case 2:
                        duplex = "长边双面";
                        break;
                    case 3:
                        duplex = "短边双面";
                        break;
                }

                //纵横向
                string orientation = "获取失败";
                switch (devm.dmOrientation)
                {
                    case 1:
                        orientation = "纵向";
                        break;
                    case 2:
                        orientation = "横向";
                        break;
                }
                //颜色
                string color = "获取失败";
                switch (devm.dmColor)
                {
                    case 1:
                        color = "黑白";
                        break;
                    case 2:
                        color = "彩色";
                        break;
                }

                Console.WriteLine($"打印机名={jobinfo2.pPrinterName}");
                Console.WriteLine($"作业编号={jobID}");
                Console.WriteLine($"文档名={jobinfo2.pDocument}");
                Console.WriteLine($"纸张类型={devm.dmFormName}");
                Console.WriteLine($"双面类型={duplex}");
                Console.WriteLine($"版面=");
                Console.WriteLine($"纵横向={orientation}");
                Console.WriteLine($"颜色={color}");
                Console.WriteLine($"打印页数={devm.dmCopies}");



            }
        }
    }
}
